#include <iostream>
#include <sys/time.h>
#include <sys/resource.h>


class BaseTimer {
public:
	enum {
		MSPSEC = 1000000  // microsecond per second
	};

	BaseTimer() {_start_t = 0;}

	// -- Clear timer :
	inline void clear() { _t = 0; }

	// -- total amount of second spent
	inline double time() const { return _t; }

	// -- Return a value to initialize random generator
	static long seed();

	// -- basic methods:
	std::ostream &print (std::ostream &) const;

	// -- Some arithmetic operators to compute cumulative time :
	BaseTimer& operator = (const BaseTimer & T) ;
	const BaseTimer operator - (const BaseTimer & T)  const;
	const BaseTimer operator - () ;
	const BaseTimer operator +  (const BaseTimer & T)  const;
	BaseTimer& operator += (const BaseTimer & T) { return *this = *this + T; };
	BaseTimer& operator -= (const BaseTimer & T) { return *this = *this - T; };

public:
	double _start_t;  // time as of start ()
	double _t;        // time
};

inline std::ostream &operator << (std::ostream &o, const BaseTimer &BT)
{
       	return BT.print(o);
}

class RealTimer : public BaseTimer {
public:
	inline RealTimer (const BaseTimer &BT) : BaseTimer (BT) {};
	inline RealTimer () {};
	void start ();
	void stop ();
};


class UserTimer : public BaseTimer {
public:
	inline UserTimer (const BaseTimer &BT) : BaseTimer (BT) {};
	inline UserTimer () {};
	void start ();
	void stop ();
};


class SysTimer : public BaseTimer {
public:
	inline SysTimer (const BaseTimer &BT): BaseTimer (BT) {};
	inline SysTimer () {};
	void start ();
	void stop ();
};


class Timer {
public :

	Timer() { rt.clear(); ut.clear(); st.clear(); _count = 0; }

	// Clear timer :
	void clear();

	// Start timer
	void start ();

	// Stop timer
	void stop ();

	// total amount of second spent in user mode
	double usertime () const { return ut.time(); }

	// total amount of second spent in system mode
	double systime () const { return st.time(); }

	// real total amount of second spent.
	double realtime () const { return rt.time(); }

	// retourne une petite graine
	// long seed() const { return RealTimer::seed(); }

	// Some arithmetic operators to compute cumulative time :
	Timer& operator = (const Timer & T) ;
	const Timer operator - (const Timer & T)  const;
	const Timer operator - () ;
	const Timer operator + (const Timer & T)  const;
	Timer& operator += (const Timer & T) { return *this = *this + T; };
	Timer& operator -= (const Timer & T) { return *this = *this - T; };

	// -- methods :
	std::ostream &print (std::ostream &) const;

	size_t count() const {return _count;}

private:
	size_t _count; // how many

	RealTimer rt;
	UserTimer ut;
	SysTimer  st;
};

#ifdef _OPENMP
#include <omp.h>
struct OMPTimer {
	double _c;
	void start() { _c = omp_get_wtime(); }
	void stop() { _c = omp_get_wtime() - _c; }
	void clear() { _c = 0.0; }
	double usertime() { return _c; }
	friend std::ostream& operator<<(std::ostream& o, const OMPTimer& t) {
		return o << t._c << 's';
	}

	OMPTimer& operator+=(const OMPTimer& t) {
		_c += t._c;
		return *this;
	}

};
#endif


// Return a value to initialize random generator
long BaseTimer::seed()
{
	struct timeval tp;
	gettimeofday(&tp, 0) ;
	return(tp.tv_usec);
}

// Output the value of the timer :
std::ostream& BaseTimer::print( std::ostream& o ) const
{ return o << _t ; }

// Some arithmetic operator :
BaseTimer& BaseTimer::operator = (const BaseTimer & T)
{
	_t = T._t ;
	return *this ;
}

// Computes and returns interval of time
// beteween *this and T
const BaseTimer BaseTimer::operator - (const BaseTimer & T) const
{
	BaseTimer Tmp ;
	Tmp._t = _t - T._t ;
	return Tmp ;
}

const BaseTimer BaseTimer::operator - ()
{
	BaseTimer Tmp ;
	Tmp._t = -_t ;
	return Tmp ;
}

const BaseTimer BaseTimer::operator + (const BaseTimer & T)  const
{
	BaseTimer Tmp ;
	Tmp._t = _t + T._t ;
	return Tmp ;
}

// Start timer
void RealTimer::start()
{
	struct timeval tmp2 ;
	gettimeofday (&tmp2, 0) ;

	// real time
	_start_t = (double) tmp2.tv_sec +
	((double) tmp2.tv_usec)/ (double)BaseTimer::MSPSEC ;
}


// Stop timer
void RealTimer::stop()
{
	struct timeval tmp2 ;
	gettimeofday (&tmp2, 0) ;

	// real time
	_t = (double) tmp2.tv_sec +
	((double) tmp2.tv_usec)/ (double)BaseTimer::MSPSEC - _start_t ;
}

// Start timer
void UserTimer::start()
{
	struct rusage  tmp1 ;  // to getrusage (sys+user times)
	getrusage (RUSAGE_SELF, &tmp1) ;
	// user time
	_start_t = (double) tmp1.ru_utime.tv_sec +
	((double) tmp1.ru_utime.tv_usec)/ (double)MSPSEC ;
}


// Stop timer
void UserTimer::stop()
{
	struct rusage  tmp1 ;  // to getrusage (sys+user times)
	getrusage (RUSAGE_SELF, &tmp1) ;
	// user time
	_t = (double) tmp1.ru_utime.tv_sec +
	((double) tmp1.ru_utime.tv_usec)/ (double)MSPSEC - _start_t ;
}


// Start timer
void SysTimer::start()
{
	struct rusage  tmp1 ;  // to getrusage (sys+user times)
	getrusage (RUSAGE_SELF, &tmp1) ;
	// user time
	_start_t = (double) tmp1.ru_stime.tv_sec +
	((double) tmp1.ru_stime.tv_usec)/ (double)MSPSEC ;
}


// Stop timer
void SysTimer::stop()
{
	struct rusage  tmp1 ;  // to getrusage (sys+user times)
	getrusage (RUSAGE_SELF, &tmp1) ;
	// user time
	_t = (double) tmp1.ru_stime.tv_sec +
	((double) tmp1.ru_stime.tv_usec)/ (double)MSPSEC - _start_t ;
}



// Clear timer :
void Timer::clear()
{ rt.clear() ; ut.clear(); st.clear(); _count = 0; }

// Start timer
void Timer::start()
{ rt.start() ; ut.start(); st.start(); _count = 0; }

// Stop timer
void Timer::stop()
{ rt.stop() ; ut.stop(); st.stop(); _count = 1; }


std::ostream& Timer::print( std::ostream& o ) const
{
	o << "user time: " << usertime() << '\n' ;
	o << "sys. time: " << systime() << '\n' ;
	return o << "real time: " << realtime() << std::endl ;
}

// Some arithmetic operator :
Timer& Timer::operator = (const Timer & T)
{
	ut = T.ut ;
	st = T.st ;
	rt = T.rt ;
	_count = T._count;
	return *this ;
}

// Comput._tes and returns interval of time
// beteween *this and T
const Timer Timer::operator - (const Timer & T)  const
{
	Timer Tmp ;
	Tmp.ut = ut - T.ut ;
	Tmp.st = st - T.st ;
	Tmp.rt = rt - T.rt ;
	Tmp._count = _count - T._count;
	return Tmp ;
}

const Timer Timer::operator - ()
{
	Timer Tmp ;
	Tmp.ut = -ut ;
	Tmp.st = -st ;
	Tmp.rt = -rt ;
	Tmp._count = - _count;
	return Tmp ;
}

const Timer Timer::operator + (const Timer & T)  const
{
	Timer Tmp ;
	Tmp.ut = ut + T.ut ;
	Tmp.st = st + T.st ;
	Tmp.rt = rt + T.rt ;
	Tmp._count = _count + T._count;
	return Tmp ;
}


// inline std::ostream &operator << (std::ostream &o, const Timer &T)
// 	{ return T.print (o); }

inline std::ostream &operator << (std::ostream &o, const Timer &T)
{
	double ut = T.usertime();
	if (ut < 0.0000000001) ut = 0;
	return o << T.realtime() << "s (" << ut << " cpu) [" << T.count() << "]";
}



/* -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
// vim:sts=8:sw=8:ts=8:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s:syntax=cpp.doxygen:foldmethod=syntax
